/*
**  File: PrintGraph.c
*/

#include <limits.h>
#include <float.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "General.h"

/* BEGIN_EXTH */
#include "PrintGraphTypes.h"
/* END_EXTH */

#include "PrintGraph.h"
static MyData *Compress( MyData *y, int *xmin, int *xmax, int *dx, 
                         const int nx, Boolean skipZero );
static void Graph( MyData *y, int xmin, int xmax, const int dx, 
                   const int nx, const int ny, Boolean skipZero, 
	           Card *txt, int ntxt );

#define	ndefx	40
#define	ndefy	20
#define DEBUG

/* BEGIN_EXTH */

/****************  PrintGraph  ****************/

void PrintGraph( MyData *y, int xmin, int xmax, const int dx, 
                 int nx, int ny, Boolean compress, Boolean skipZero, 
		 Card *txt, int ntxt )
{
    MyData   *newy = y;
    int      newdx = dx;

    if ( nx <= 0 ) nx = ndefx;
    if ( ny <= 0 ) ny = ndefy;
    if ( compress ) newy = Compress( y, &xmin, &xmax, &newdx, nx, skipZero );
    Graph( newy, xmin, xmax, newdx, nx, ny, skipZero, txt, ntxt );
    if ( compress ) free( newy );
}

/* END_EXTH ----------------------- only private functions follow */

/****************  Compress  ****************/

MyData  *Compress( MyData *y, int *xmin, int *xmax, int *dx, 
                   const int nx, Boolean skipZero )
{
    MyData   *ystart = y, *newy, *ny, *yfstptr, *ylstptr;
    int      np, x, cf = 1;

/* if skipZero, determine new xmin and xmax */

    np = 1 + ( *xmax - *xmin ) / *dx ;
    if ( skipZero )
    {
	x = *xmin;
	while ( *y == 0 && x < *xmax )
	{
	    x++;
	    y++;
	}
	*xmin = x;
	yfstptr = y;

	x = *xmax;
	y = ystart + np - 1;
	while ( *y == 0 && x > *xmin )
	{
	    x--;
	    y--;
	}
	*xmax = x;
	ylstptr = y;
    }
    else
    {
	yfstptr = y;
	ylstptr = &y[np-1];
    }

/* calculate np and compression factor */

    np = 1 + ( *xmax - *xmin ) / *dx ;
    if ( np > nx )
    {
	cf = (int)ceil( (float)np/(float)nx );
	if ( np/cf > nx )
	{
	    printf( "ERROR in Printgraph/Compress: np/cf>nx: np=%i, nx=%i, cf=%i\n", np, nx, cf );
	    exit( 1 );
	}
    }
    if ( np != (int)( ylstptr - yfstptr ) + 1 )
    {
	printf( "ERROR in Printgraph/Compress: number of points %i differs from delta-yptr %i\n", 
	        np, (int)( ylstptr - yfstptr ) + 1 );
	exit( 1 );
    }
    
    *dx *= cf;

/* allocate and fill newy with (un)compressed data */

    newy = NEW( MyData, nx );
    if ( !newy )
    {
	puts( "Printgraph: Unable to allocate memory" );
	exit( 1 );
    }
    ny = newy;
    y = yfstptr;
    if ( np <= nx )
	while ( np-- )
	    *ny++ = *y++;
    else
    {
	np = np/cf;
	while ( np-- )
	{
	    x = cf;
	    while ( x && y <= ylstptr )
	    {
		*ny += *y++;        /* sum the values */
		x--;
	    }
	    if ( x == cf )
	    {
		printf( "ERROR in Printgraph/Compress: x==cf" );
		exit( 1 );
	    }
	    else
		*ny++ /= cf - x;    /* take the average */
		                    /* in present implementation an incomplete
				       last bin does not occur */
	}
    }
    
    return( newy );
}

/****************  Graph  ****************/

void Graph( MyData *y, int xmin, int xmax, const int dx, 
            const int nx, const int ny, Boolean skipZero, 
	    Card *txt, int ntxt )
{
    char     *pict, *p;
    int      np, i, j, k, x, jzero, dbg = 1;
    MyData   ymin, ymax, dy, *ystart = y, *yfstptr, *ylstptr;

/* if skipZero, determine new xmin and xmax */

    np = 1 + ( xmax - xmin ) / dx ;
    if ( skipZero )
    {
	x = xmin;
	while ( *y == 0 && x < xmax )
	{
	    x++;
	    y++;
	}
	xmin = x;
	yfstptr = y;

	x = xmax;
	y = ystart + np - 1;
	while ( *y == 0 && x > xmin )
	{
	    x--;
	    y--;
	}
	xmax = x;
	ylstptr = y;
    }
    else
    {
	yfstptr = y;
	ylstptr = &y[np-1];
    }

/* determine number of points */

    np = 1 + ( xmax - xmin ) / dx ;
    if ( np > nx )
    {
	printf( "Number of points exceeds horizontal size <nx> = %i, use compress option\n", nx );
	return;
    }
    if ( np != (int)( ylstptr - yfstptr ) + 1 )
    {
	printf( "ERROR in Printgraph/Graph: number of points %i differs from delta-yptr %i\n", 
	        np, (int)( ylstptr - yfstptr ) + 1 );
	exit( 1 );
    }

/* determine ymin and ymax */

#ifdef MYDATAisFLOAT
    ymax = FLT_MIN;
    ymin = FLT_MAX;
#endif
#ifdef MYDATAisINT
    ymax = INT_MIN;
    ymin = INT_MAX;
#endif
#ifdef MYDATAisLONG
    ymax = LONG_MIN;
    ymin = LONG_MAX;
#endif
    y = yfstptr;
    while ( y <= ylstptr )
    {
	if ( *y > ymax ) ymax = *y;
	if ( *y < ymin ) ymin = *y;
	y++;
    }
    dy = ( ymax - ymin ) / ( ny - 1 );
    if ( dy * ( ny - 1 ) < ( ymax - ymin ) )
	dy += 1;

/* allocate picture : pict[ny][nx+1] */

    pict = NEW( char, ny*(nx+1) );
    if ( !pict )
    {
	puts( "Printgraph: Unable to allocate memory" );
	exit( 1 );
    }

/* fill with blanks and divide into lines */

    j = ny * ( nx+1 );
    p = pict;
    while ( j-- )
	*p++ = ' ';
    p = pict + np;        /* move to end of line (np is actual number of x-points) */
    while ( j++ < ny )      
    {
	*p = '\0';
	p += nx+1;        /* move to next line (nx+1 is allocated x-size ) */
    }

/* draw horizontal axis */

    jzero = ( 0 - ymin ) / dy;
    p = pict + jzero*(nx+1);
    for ( i=0; i<np; i++ )
	*p++ = '-';

/* draw vertical bars */

    i = 0;
    y = yfstptr;
    while ( y <= ylstptr )
    {
	p = pict + i + jzero*(nx+1);
	j = ( *y - ymin ) / dy;
	if ( j >= jzero )
	{
	    for ( k=jzero; k<=j; k++ )
	    {
		if ( k == jzero && *y != 0 )
		{
		    p += nx+1;
		    continue;
		}
		*p = '*';
		if ( *y == 0 ) *p = '+';
		p += nx+1;
	    }
	}
	else
	{
	    for ( k=jzero; k>=j; k-- )
	    {
		if ( k == jzero && *y != 0 )
		{
		    p -= nx+1;
		    continue;
		}
		*p = '*';
		if ( *y == 0 ) *p = '+';
		p -= nx+1;
	    }
	}
	i++;
	y++;
    }

/* output */

    j = ny;
    p = pict + (j-1)*(nx+1);
    while ( j-- )
    {
	printf( "%s      ", p );
	p -= nx+1;
	if ( ntxt )
	{
	    printf( "%s", *txt++ );
	    ntxt--;
	}
#ifdef DEBUG
	else
	{
	    if ( dbg == 2 ) printf("xmin=%i  xmax=%i  dx=%i", xmin, xmax, dx );
#ifdef MYDATAisFLOAT
	    if ( dbg == 3 ) printf("ymin=%f  ymax=%f  dy=%f", ymin, ymax, dy );
#endif
#ifdef MYDATAisINT
	    if ( dbg == 3 ) printf("ymin=%i  ymax=%i  dy=%i", ymin, ymax, dy );
#endif
#ifdef MYDATAisLONG
	    if ( dbg == 3 ) printf("ymin=%li  ymax=%li  dy=%li", ymin, ymax, dy );
#endif
	    if ( dbg == 4 ) printf("np=%i nx=%i  ny=%i", np, nx, ny );
	    dbg++;
	}
#endif
	putchar( '\n' );
    }

    free( pict );
}
